#ifndef  _FEATURE_TASK_H_
#define  _FEATURE_TASK_H_

#include "feature.h"

#include <omp.h>
#include <fstream>
#include <io.h>  
#include <iostream>
#include <sstream>
#include <cstdint>
#include <map>
#include <set>


class FeatPair {
public:
  FeatPair();
  // delete newed Feature
  ~FeatPair();

  void clear();
  bool empty();

  // load image, object pointed by feat_1 and feat_2 must be newed
  bool LoadFeat(Feature* feat_1, Feature* feat_2);
  (Feature*)& feat_1() { return feat_1_; }
  const Feature* const feat_1() const { return feat_1_; }
  (Feature*)& feat_2() { return feat_2_; }
  const Feature* const feat_2() const { return feat_2_; }

  // extract feature point and descriptor
  bool ExtractFeature(bool with_desc);

  // match feat when two feat are the same kind
  bool MatchFeat(bool save_fund = false, bool show_img = false);

  // clear matches and inliers
  void ClearMatches();

  const std::vector<cv::DMatch>& matches() const {
    return matches_;
  }

  size_t matches_size() const {
    return matches_.size();
  }

  const std::vector<char>& inliers() const {
    return inliers_;
  }

  // return the inlier number
  size_t CountInliers() const;

  // find neighboring feat_2
  bool FindNeighbor(int k);
  void GetFullNeighbor(std::vector<std::vector<std::pair<int, float>>>* feat_1_feat_2) const;
  void LoadFullNeighbor(const std::vector<std::vector<std::pair<int, float>>>& feat_1_feat_2);

  // get neighbors by distant threshold
  void GetNeighbor(float thres, std::map<int, std::vector<int>> *map_feat1_feat2) const;
  // get neighbors by number
  void GetNeighbor(int k, std::map<int, std::vector<int>> *map_feat1_feat2) const;

  // transform feat
  void Transform(FeatPair& feat_pair_2);

  size_t feat_1_size() const {
    return feat_1_->keypoint().size();
  }
  size_t feat_2_size() const {
    return feat_2_->keypoint().size();
  }

  // show image for repeat
  void ShowImgPair(bool with_match);

  // calculate epilines
  bool CalculateEpilines(const cv::Matx33f& fund);

  const std::vector<cv::Vec<float, 3>>& epilines() const { return epilines_; }

  const cv::Matx33f& fundamental() { return fundamental_; }

  size_t CountInliersHomography(const cv::Matx33f& homo);

  const float homo_thres_squared() const { return homo_thres_pixels_squared_; }

private:
  Feature* feat_1_;
  Feature* feat_2_;
  // feature ratio test threshold
  float ratio_;
  float homo_thres_pixels_squared_;

  // for guided matching
  std::vector<cv::DMatch> matches_; // putative matches
  std::vector<char> inliers_;
  cv::Matx33f fundamental_;

  // feat_1 corresponding epilines
  std::vector<cv::Vec<float, 3>>  epilines_;

  // for feature repeatability test
  // map feat1 to all neighboring feat2
  std::vector<std::vector<std::pair<int, float>>> feat_1_feat_2_;
};

// main class for image collection task
class ImgCollectionTask {
public:
  ImgCollectionTask();
  ~ImgCollectionTask();

  // load image file names from folder or vectors
  bool LoadImgFileNames(const std::string& filefolder, const std::string& img_types = "jpg");
  bool LoadImgFileNames(const std::vector<std::string>& img_files);

  // for repeatability task
  void LoadThresVec(const std::vector<float>& thres_vec);
  void ImgCollectionRepeatTask(std::ostream& sout = std::cout, bool feat1_sift = true, bool show_img = false);

  // for guided matching
  bool LoadHomograhpy(const std::string& folder);
  bool GuidedMatchTask(bool(ImgCollectionTask::*ptr_guided_task)(const int, bool), std::ostream& sout = std::cout, bool show_img = false);
  bool SiftGuidedMatch(const int idx, bool show_img);

  // for geometry guided matching
  bool GeometryGuidedMatch(const int idx, bool show_img);

private:
  // general
  std::vector<std::string> img_files_;
  std::vector<float> thres_vec_;
  template<class FeatType> FeatType* AllocateFeature();

  // for repeatability task
  std::vector<std::vector<std::vector<float>>> repeat_results_;
  void ImgRepeatTask(size_t idx, bool feat1_sift, bool show_img);

  // for guided matching task
  std::vector<cv::Matx33f> homography_;
  std::vector<std::vector<std::vector<float>>> guided_results_;
  std::vector<std::pair<int, int>> pairs_;
  float guided_ratio_;

};


#endif // ! _FEATURE_TASK_H_
